import com.kirillam.handy.datastructures.WeakReference;
import com.kirillam.handy.events.HATEvent;


/** Class for representing a Set datastructure that uniquely stores objects. */

class com.kirillam.handy.datastructures.Set
{
	// VARIABLES
	
	/* List of all the set members. */
	
	private var members	: Object;
	
	
	// PROPERTIES
	
	private var _size	: Number = 0;
	
	/** Retrieves the number of members in the set. */
	
	public function get size() : Number
	{
		return _size;
	}
	
	
	
	/** Constructor */
	
	public function Set()
	{
		members	= new Object();
	}
	
	
	/** 
	 * Add an object to the set. Same object won't be added twice.
	 * 
	 * @param	o					Object to add.
	 * @param	useWeakReference	If true then the object is stored in a weak manner allowing it to
	 * 								be garbage collected if there's no other reference to it outside this set.
	 */

	public function add( o : Object, useWeakReference : Boolean )
	{
		if( members[ o.hatId() ] == null ) _size++;
		
		if( useWeakReference ) {
			members[ o.hatId() ]	= new WeakReference( o );
			members[ o.hatId() ].handle( HATEvent.REMOVED, this, onObjectGarbageCollected, true );
		} else {
			members[ o.hatId() ]	= o;
		}
	}
	
	
	/* Handles the HATEvent.REMOVED when a member gets garbage collected and removes the reference to it from the set. */
	
	private function onObjectGarbageCollected( e : HATEvent )
	{
		if( members[ e.target.objectId ] != null ) {
			_size--;
			delete members[ e.target.objectId ];
		}
	}
	
	
	/** 
	 * Removes an object from the set.
	 * 
	 * @param	o	Object to remove.
	 */
	
	public function remove( o : Object )
	{
		if( members[ o.hatId() ] != null ) _size--;
		
		if( members[ o.hatId() ] !== o ) {
			members[ o.hatId() ].object = null;
			members[ o.hatId() ].ignore( HATEvent.REMOVED, this );
		}
		delete members[ o.hatId() ];
	}
	
	
	/** Empties the set. */
	
	public function removeAll()
	{
		forEach( this, function( o : Object )
		{
			if( members[ o.hatId() ] !== o ) {
				members[ o.hatId() ].object = null;
				members[ o.hatId() ].ignore( HATEvent.REMOVED, this );
			}
		} );
		
		members	= new Object();
		_size	= 0;
	}
	
	
	/** 
	 * Is the object in the set.
	 * 
	 * @param	o	Object to remove.
	 */
	
	public function isIn( o : Object ) : Boolean
	{
		return members[ o.hatId() ] != null;
	}
	
	
	/**
	 * Takes out an object from the set.
	 * 
	 * @return A former member of the set.
	 */
	
	public function takeOut() : Object
	{
		if( _size <= 0 ) return null;
		
		for( var id : String in members ) {
			
			var o	: Object	= getMemberById( id );
			remove( o );
			return o;
		}
		
		return null;
	}
	
	
	/**
	 * Retrieves a member by its hatId.
	 * 
	 * @param	id	Object's hatId.
	 * @return The object in the set with the given hatId.
	 */
	
	public function getMemberById( id : String ) : Object
	{
		if( members[ id ].hatId() != id ) return members[ id ].object;
		return members[ id ];
	}
	
	
	/**
	 * For each member of the set executes a method passing the member into it as an argument.
	 * Meant to allow functionality of a for( .. in .. ) loop.
	 * 
	 * @param	target	Object that has the method to call.
	 * @param	method	Method to call. It will be called passing each member as a single argument.
	 */
	
	public function forEach( target : Object, method : Function )
	{
		for( var id : String in members ) {
			method.call( target, getMemberById( id ) );
		}
	}
}